/******************************************************************************
 * Copyright (C) 2011 ShenZhen ComTop Information Technology Co.,Ltd
 * All Rights Reserved.
 * 本软件为深圳康拓普开发研制。未经本公司正式书面同意，其他任何个人、团体不得使用、
 * 复制、修改或发布本软件.
 *****************************************************************************/

package com.code.common.security;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.Cipher;

import org.apache.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.code.common.utils.StringUtils;

/**
 * <b>请使用<code>com.comtop.top.component.common.util.RSAUtil</code>代替。</b>
 * <p>
 * RSA加密工具类
 * 
 * @author 刘薇
 * @since 1.0
 * @version 2011-8-29 刘薇
 */

@Deprecated
public final class RSATools {
    
    /**
     * 构造函数
     */
    private RSATools() {
    }
    
    /** logger对象 */
    private static final Logger LOGGER = Logger.getLogger(RSATools.class);
    
    /** 加密计数 */
    private static int encryptCount = 0;
    
    /** 加密密码 */
    private static Cipher encryptCipher = null;
    
    /** 解密计数 */
    private static int decryptCount = 0;
    
    /** 解密密码 */
    private static Cipher decryptCipher = null;
    
    /**
     * 生成密钥对
     * 
     * @return KeyPair
     */
    public static KeyPair generateKeyPair() {
        try {
            KeyPairGenerator objKeyPairGen = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
            final int iKeySize = 128;
            objKeyPairGen.initialize(iKeySize, new SecureRandom());
            return objKeyPairGen.genKeyPair();
        } catch (Exception e) {
            LOGGER.error("生成密钥对出错：", e);
            return null;
        }
    }
    
    /**
     * 生成公钥
     * 
     * @param modulus byte[]
     * @param publicExponent byte[]
     * @return RSAPublicKey
     */
    public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) {
        KeyFactory objKeyFac = null;
        try {
            objKeyFac = KeyFactory.getInstance("RSA", new BouncyCastleProvider());
        } catch (NoSuchAlgorithmException ex) {
            LOGGER.error("无此产生公钥的算法：", ex);
            return null;
        }
        
        RSAPublicKeySpec objPubKeySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent));
        try {
            return (RSAPublicKey) objKeyFac.generatePublic(objPubKeySpec);
        } catch (InvalidKeySpecException ex) {
            LOGGER.error("生成公钥的参数不合法：", ex);
            return null;
        }
    }
    
    /**
     * 生成私钥
     * 
     * @param modulus byte[]
     * @param privateExponent byte[]
     * @return RSAPrivateKey
     */
    public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) {
        KeyFactory objKeyFac = null;
        try {
            objKeyFac = KeyFactory.getInstance("RSA", new BouncyCastleProvider());
        } catch (NoSuchAlgorithmException ex) {
            LOGGER.error("无此产生私钥的算法：", ex);
            return null;
        }
        
        RSAPrivateKeySpec objPriKeySpec =
            new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent));
        try {
            return (RSAPrivateKey) objKeyFac.generatePrivate(objPriKeySpec);
        } catch (InvalidKeySpecException ex) {
            LOGGER.error("生成私钥的参数不合法：", ex);
            return null;
        }
    }
    
    /**
     * 序列化加密数据
     * 
     * @param key Key 加密的密钥
     * @param data byte[] 待加密的明文数据
     * @return byte[] 加密后的数据
     */
    synchronized public static byte[] encrypt(Key key, byte[] data) {
        Key objTempKey = key;
        try {
            AbstractCreateRSAKey objCereateKey = getCreateRSAKeyInstance();
            if (objCereateKey != null) {
                objTempKey = objCereateKey.createRSAPrivateKey();
            }
        } catch (Exception e) {
            LOGGER.error("自定义创建密钥时出错！", e);
        }
        try {
            if (encryptCount == 0) {
                encryptCipher = Cipher.getInstance("RSA", new BouncyCastleProvider());
                encryptCipher.init(Cipher.ENCRYPT_MODE, objTempKey);
                encryptCount++;
            }
            int iBlockSize = encryptCipher.getBlockSize();// 获得加密块大小
            int iOutputSize = encryptCipher.getOutputSize(data.length);// 获得加密块加密后块大小
            int iLeavedSize = data.length % iBlockSize;
            int iBlocksSize = iLeavedSize != 0 ? data.length / iBlockSize + 1 : data.length / iBlockSize;
            byte[] byteRaw = new byte[iOutputSize * iBlocksSize];
            int i = 0;
            while (data.length - i * iBlockSize > 0) {
                if (data.length - i * iBlockSize > iBlockSize) {
                    encryptCipher.doFinal(data, i * iBlockSize, iBlockSize, byteRaw, i * iOutputSize);
                } else {
                    encryptCipher.doFinal(data, i * iBlockSize, data.length - i * iBlockSize, byteRaw, i * iOutputSize);
                }
                // 这里面doUpdate方法不可用，查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到ByteArrayOutputStream中，
                // 而最后doFinal的时候才将所有的byte[]进行加密，可是到了此时加密块大小很可能已经超出了OutputSize所以只好用dofinal方法。
                i++;
            }
            return byteRaw;
        } catch (Exception e) {
            LOGGER.error("加密出错：", e);
            return null;
        }
    }
    
    /**
     * 数据加密
     * 
     * @param key 加密的密钥
     * @param data 待加密的明文数据
     * @return 加密后的数据
     */
    public static String encrypt(Key key, String data) {
        
        String strDestination = "";
        try {
            if (StringUtils.isNotBlank(data)) {
                strDestination = bytesToHexString(RSATools.encrypt(key, data.getBytes("UTF-8")));
            }
        } catch (UnsupportedEncodingException e) {
            LOGGER.error("编码错误：", e);
            return null;
        }
        return strDestination;
    }
    
    /**
     * 序列化解密后的明文
     * 
     * @param key 解密的密钥
     * @param raw 已经加密的数据
     * @return 解密后的明文
     */
    synchronized public static byte[] decrypt(Key key, byte[] raw) {
        Key objTempKey = key;
        try {
            AbstractCreateRSAKey objCereateKey = getCreateRSAKeyInstance();
            if (objCereateKey != null) {
                objTempKey = objCereateKey.createRSAPublicKey();
            }
        } catch (Exception e) {
            LOGGER.error("自定义创建密钥时出错！", e);
        }
        try {
            if (decryptCount == 0) {
                decryptCipher = Cipher.getInstance("RSA", new BouncyCastleProvider());
                decryptCipher.init(Cipher.DECRYPT_MODE, objTempKey);
                decryptCount++;
            }
            int iBlockSize = decryptCipher.getBlockSize();
            ByteArrayOutputStream objBout = new ByteArrayOutputStream(64);
            int j = 0;
            
            while (raw.length - j * iBlockSize > 0) {
                objBout.write(decryptCipher.doFinal(raw, j * iBlockSize, iBlockSize));
                j++;
            }
            return objBout.toByteArray();
        } catch (Exception e) {
            LOGGER.error("解密出错：", e);
            return null;
        }
    }
    
    /**
     * 密码解密
     * 
     * @param key 解密的密钥
     * @param raw 已经加密的数据
     * @return 解密后的明文
     */
    public static String decrypt(Key key, String raw) {
        String strDestination = "";
        try {
            if (StringUtils.isNotBlank(raw)) {
                byte[] bKey = RSATools.decrypt(key, hexStringToByte(raw));
                if (bKey != null && bKey.length > 0) {
                    strDestination = new String(bKey, "UTF-8");
                } else {
                    LOGGER.error("已经加密的数据raw：[" + raw + "]解密出错");
                }
            }
        } catch (UnsupportedEncodingException e) {
            LOGGER.error("字符串编码错误：", e);
            return null;
        }
        return strDestination;
    }
    
    /**
     * 把字节数组转换成16进制字符串
     * 
     * @param bArray 字符数组
     * @return 16进制字符串
     */
    public static final String bytesToHexString(byte[] bArray) {
        StringBuffer sbHex = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2) {
                sbHex.append(0);
            }
            sbHex.append(sTemp.toUpperCase());
        }
        return sbHex.toString();
    }
    
    /**
     * 把16进制字符串转换成字节数组
     * 
     * @param hex 16进制字符串
     * @return 字符数组
     */
    public static byte[] hexStringToByte(String hex) {
        int iLen = hex.length() / 2;
        byte[] byteResult = new byte[iLen];
        char[] chAchar = hex.toCharArray();
        for (int i = 0; i < iLen; i++) {
            int iPos = i * 2;
            byteResult[i] = (byte) (toByte(chAchar[iPos]) << 4 | toByte(chAchar[iPos + 1]));
        }
        return byteResult;
    }
    
    /**
     * 把16字符转换成字节
     * 
     * @param c 16字符
     * @return 字节
     */
    private static byte toByte(char c) {
        byte byteTo = (byte) "0123456789ABCDEF".indexOf(c);
        return byteTo;
    }
    
    /**
     * 读取配置项配置的创建密钥的类名，并通过反射得到实例。
     * 
     * @return 创建密钥的类实例
     */
    private static AbstractCreateRSAKey getCreateRSAKeyInstance() {
        AbstractCreateRSAKey objCreateRSAKey = null;
        try {
            // 后续需要进行修改
            String strClassName = "N";
            
            // 配置项配置的值为N时，表示不需要自定义密钥。
            if ("N".equalsIgnoreCase(strClassName) || "".equalsIgnoreCase(strClassName)) {
                return null;
            }
            
            // 通过反射，得到创建密钥的类实例。
            Class<?> objC = Class.forName(strClassName);
            objCreateRSAKey = (AbstractCreateRSAKey) objC.newInstance();
            
        } catch (Exception e) {
            LOGGER.error("生成创建密钥的类实例时出错！请检查ct-config.xml里的createRSAKey配置是否正确，", e);
        }
        return objCreateRSAKey;
    }
}